Crash治理:图片缩放pointerIndex out of range.

背景: API28,PhotoView+Viewpager

场景: 对图片进行双指缩放操作.

异常:

E AndroidRuntime: java.lang.IllegalArgumentException: pointerIndex out of range
E AndroidRuntime: at android.view.MotionEvent.nativeGetAxisValue(Native Method)
E AndroidRuntime: at android.view.MotionEvent.getX(MotionEvent.java:2205)
E AndroidRuntime: at androidx.viewpager.widget.ViewPager.onInterceptTouchEvent(ViewPager.java:2072)
E AndroidRuntime: at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2573)
E AndroidRuntime: at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3030)

原因: 根据上面异常信息可以发现出错位置在ViewPager.java2072行ev.getX(pointerIndex)(如下),而这次的异常也就是其中的参数(pointerIndex)超出了范围导致,pointerIndex每次通过MotionEvent.findPointerIndex(activePointerId)来获取,在双指对图片进行双指缩放时,一个手指抬起,activePointerId却没有进行重新赋值,获取到值错误,就直接影响了pointerIndex,也就在这个时候出现了异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {

...

switch (action) {
case MotionEvent.ACTION_MOVE: {
...
final int activePointerId = mActivePointerId;
if (activePointerId == INVALID_POINTER) {
// If we don't have a valid id, the touch down wasn't on content.
break;
}

final int pointerIndex = ev.findPointerIndex(activePointerId);
final float x = ev.getX(pointerIndex);

...
}

解决方案:
查阅了资料,比较简单明了的处理方法就是自定义Viewpager对ViewPager的onInterceptTouchEvent()进行try{}catch()操作。值得注意的是,在try{}catch()中直接return super.onInterceptTouchEvent(ev),图片会出现滑动异常,所以进行了如下的特殊处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class CustomViewPager extends ViewPager {
public CustomViewPager(@NonNull Context context) {
super(context);
}

public CustomViewPager(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean isIntercept = false;
try {
isIntercept = super.onInterceptTouchEvent(ev);
} catch (IllegalArgumentException exception) {
exception.printStackTrace();

}
return isIntercept;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
try {
super.onTouchEvent(ev);
} catch (IllegalArgumentException exception) {
exception.printStackTrace();

}
return false;
}
}